package main

import (
	"go/ast"
)

type Field struct {
	Name      string
	FieldType string
}

type Type struct {
	Name   string
	Fields []*Field
}

type File struct {
	PackageName string
	Imports     []string
	Types       []*Type
}

type typeVisitor struct {
	name   string
	fields []*Field
}

func (t *typeVisitor) Visit(node ast.Node) (w ast.Visitor) {
	n, ok := node.(*ast.Field)
	if !ok {
		return t
	}
	var typ string
	switch nodeTyp := n.Type.(type) {
	case *ast.Ident: // 基础类型
		typ = nodeTyp.String()
	case *ast.StarExpr: // 指针
		switch x := nodeTyp.X.(type) {
		case *ast.SelectorExpr:
			typ = "*" + nodeTyp.X.(*ast.Ident).String() + "." + x.Sel.String()
		}
	case *ast.ArrayType: // 切片
		typ = "[]" + nodeTyp.Elt.(*ast.Ident).String()
	default:
		typ = "string"
	}

	/*
		a,b,c string
	*/
	for _, name := range n.Names {
		t.fields = append(t.fields, &Field{
			Name:      name.String(),
			FieldType: typ,
		})
	}
	return t
}

type fileVisitor struct {
	packageName string
	imports     []string
	types       []*typeVisitor
}

func (f *fileVisitor) Visit(node ast.Node) (w ast.Visitor) {
	switch n := node.(type) {
	case *ast.TypeSpec:
		t := &typeVisitor{
			name: n.Name.Name,
		}
		f.types = append(f.types, t)
		return t
	case *ast.ImportSpec:
		path := n.Path.Value
		if n.Name != nil && n.Name.Name != "" {
			path = n.Name.String() + " " + path
		}
		f.imports = append(f.imports, path)
	}
	return f
}

type SingleFileVisitor struct {
	File *fileVisitor
}

func (s *SingleFileVisitor) Visit(node ast.Node) (w ast.Visitor) {
	n, ok := node.(*ast.File)
	if !ok {
		return s
	}
	s.File = &fileVisitor{
		packageName: n.Name.String(),
	}
	return s.File
}

func (s *SingleFileVisitor) Get() *File {
	types := make([]*Type, 0, len(s.File.types))
	for _, f := range s.File.types {
		types = append(types, &Type{
			Name:   f.name,
			Fields: f.fields,
		})
	}

	return &File{
		PackageName: s.File.packageName,
		Imports:     s.File.imports,
		Types:       types,
	}
}
